local Event = require 'utils.event'
local Global = require 'utils.global'
local BiterRolls = require 'modules.wave_defense.biter_rolls'
local BiterHealthBooster = require 'modules.biter_health_booster'
local WD = require 'modules.wave_defense.table'
local WPT = require 'maps.amap.table'

local traps = {}

Global.register(
    traps,
    function(t)
        traps = t
    end
)

local Public = {}
local floor = math.floor
local random = math.random
local abs = math.abs
local sqrt = math.sqrt

local spawn_amount_rolls = {}
for a = 48, 1, -1 do
    spawn_amount_rolls[#spawn_amount_rolls + 1] = floor(a ^ 5)
end

local random_particles = {
    'dirt-2-stone-particle-medium',
    'dirt-4-dust-particle',
    'coal-particle'
}

local s_random_particles = #random_particles

local function create_particles(data)
    local surface = data.surface
    local position = data.position
    local amount = data.amount

    if not surface or not surface.valid then
        return
    end
    for i = 1, amount, 1 do
        local m = random(6, 12)
        local m2 = m * 0.005

        surface.create_particle(
            {
                name = random_particles[random(1, s_random_particles)],
                position = position,
                frame_speed = 0.1,
                vertical_speed = 0.1,
                height = 0.1,
                movement = {m2 - (random(0, m) * 0.01), m2 - (random(0, m) * 0.01)}
            }
        )
    end
end

local function spawn_biters(data)
    local surface = data.surface
    if not (surface and surface.valid) then
        return
    end
    local position = data.position
    local h = floor(abs(position.y))
    local wave_number = WD.get('wave_number')
    local max_biters = WPT.get('biters')

    if max_biters.amount >= max_biters.limit then
        return
    end

    if not position then
        position = surface.find_non_colliding_position('small-biter', position, 10, 1)
        if not position then
            return
        end
    end

    local function trigger_health()
        local m = 0.0015

        m = m * 1.05

        local boosted_health = 1.25

        if wave_number <= 10 then
            wave_number = 10
        end

        boosted_health = boosted_health * (wave_number * 0.02)

        local sum = boosted_health * 5

        sum = sum + m

        if sum >= 100 then
            sum = 100
        end

        return sum
    end

    BiterRolls.wave_defense_set_unit_raffle(h * 0.20)

    local unit
    if random(1, 3) == 1 then
        unit = surface.create_entity({name = BiterRolls.wave_defense_roll_spitter_name(), position = position})
        max_biters.amount = max_biters.amount + 1
    else
        unit = surface.create_entity({name = BiterRolls.wave_defense_roll_biter_name(), position = position})
        max_biters.amount = max_biters.amount + 1
    end

    if random(1, 32) == 1 then
        local sum = trigger_health()
        max_biters.amount = max_biters.amount + 1
        BiterHealthBooster.add_boss_unit(unit, sum, 0.38)
    end
end

local function spawn_worms(data)
    local max_biters = WPT.get('biters')

    if max_biters.amount >= max_biters.limit then
        return
    end

    local surface = data.surface
    if not (surface and surface.valid) then
        return
    end
    local position = data.position
    BiterRolls.wave_defense_set_worm_raffle(sqrt(position.x ^ 2 + position.y ^ 2) * 0.20)
    surface.create_entity({name = BiterRolls.wave_defense_roll_worm_name(), position = position})
    max_biters.amount = max_biters.amount + 1
end

function Public.buried_biter(surface, position, max)
    if not (surface and surface.valid) then
        return
    end
    if not position then
        return
    end
    if not position.x then
        return
    end
    if not position.y then
        return
    end

    local amount = 8
    local a = 0
    max = max or random(4, 6)

    local ticks = amount * 30
    ticks = ticks + 90
    for t = 1, ticks, 1 do
        if not traps[game.tick + t] then
            traps[game.tick + t] = {}
        end

        traps[game.tick + t][#traps[game.tick + t] + 1] = {
            callback = 'create_particles',
            data = {surface = surface, position = {x = position.x, y = position.y}, amount = 4}
        }

        if t > 90 then
            if t % 30 == 29 then
                a = a + 1
                traps[game.tick + t][#traps[game.tick + t] + 1] = {
                    callback = 'spawn_biters',
                    data = {surface = surface, position = {x = position.x, y = position.y}}
                }
                if a >= max then
                    break
                end
            end
        end
    end
end

function Public.buried_worm(surface, position)
    if not (surface and surface.valid) then
        return
    end
    if not position then
        return
    end
    if not position.x then
        return
    end
    if not position.y then
        return
    end

    local amount = 8

    local ticks = amount * 30
    ticks = ticks + 90
    local a = false
    for t = 1, ticks, 1 do
        if not traps[game.tick + t] then
            traps[game.tick + t] = {}
        end

        traps[game.tick + t][#traps[game.tick + t] + 1] = {
            callback = 'create_particles',
            data = {surface = surface, position = {x = position.x, y = position.y}, amount = 4}
        }

        if not a then
            traps[game.tick + t][#traps[game.tick + t] + 1] = {
                callback = 'spawn_worms',
                data = {surface = surface, position = {x = position.x, y = position.y}}
            }
            a = true
        end
    end
end

local callbacks = {
    ['create_particles'] = create_particles,
    ['spawn_biters'] = spawn_biters,
    ['spawn_worms'] = spawn_worms
}

local function on_tick()
    local t = game.tick
    if not traps[t] then
        return
    end
    for _, token in pairs(traps[t]) do
        local callback = token.callback
        local data = token.data
        local cbl = callbacks[callback]
        if callbacks[callback] then
            cbl(data)
        end
    end
    traps[t] = nil
end

function Public.reset()
    for k, _ in pairs(traps) do
        traps[k] = nil
    end
end

Event.add(defines.events.on_tick, on_tick)

return Public
